package com.dragon.modules.yuegou.service.impl.coupon;

import cn.hutool.core.math.MathUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dragon.common.exception.DragonException;
import com.dragon.common.web.PageRequest;
import com.dragon.modules.yuegou.entity.coupon.CouponEntity;
import com.dragon.modules.yuegou.entity.coupon.CouponTemplateEntity;
import com.dragon.modules.yuegou.entity.coupon.MerchantCouponGrantEntity;
import com.dragon.modules.yuegou.entity.coupon.ParkApplyEntity;
import com.dragon.modules.yuegou.entity.coupon.WelfareEntity;
import com.dragon.modules.yuegou.entity.order.OrderCouponEntity;
import com.dragon.modules.yuegou.entity.product.SkuEntity;
import com.dragon.modules.yuegou.entity.product.SpuEntity;
import com.dragon.modules.yuegou.enums.CouponStatus;
import com.dragon.modules.yuegou.enums.OwnerType;
import com.dragon.modules.yuegou.enums.code.CouponCode;
import com.dragon.modules.yuegou.enums.code.ErrorCode;
import com.dragon.modules.yuegou.enums.coupon.CouponPlatform;
import com.dragon.modules.yuegou.enums.coupon.CouponSourceType;
import com.dragon.modules.yuegou.enums.coupon.CouponTemplateFormat;
import com.dragon.modules.yuegou.enums.coupon.CouponTemplateScope;
import com.dragon.modules.yuegou.enums.coupon.CouponTemplateTimeLimitType;
import com.dragon.modules.yuegou.enums.coupon.CouponTemplateType;
import com.dragon.modules.yuegou.enums.coupon.CouponType;
import com.dragon.modules.yuegou.excel.coupon.ReceiveDataWorkbook;
import com.dragon.modules.yuegou.mapper.coupon.CouponMapper;
import com.dragon.modules.yuegou.model.coupon.Coupon;
import com.dragon.modules.yuegou.model.coupon.CouponDiscountCombin;
import com.dragon.modules.yuegou.model.coupon.CouponTemplate;
import com.dragon.modules.yuegou.model.coupon.CouponTemplateRandom;
import com.dragon.modules.yuegou.model.coupon.CouponUserList;
import com.dragon.modules.yuegou.model.coupon.ParkApply;
import com.dragon.modules.yuegou.model.coupon.ReceiveDetail;
import com.dragon.modules.yuegou.model.coupon.Welfare;
import com.dragon.modules.yuegou.model.order.Order;
import com.dragon.modules.yuegou.model.product.Sku;
import com.dragon.modules.yuegou.model.system.Document;
import com.dragon.modules.yuegou.properties.CouponProperties;
import com.dragon.modules.yuegou.properties.ResetTimeProperties;
import com.dragon.modules.yuegou.query.coupon.AvailableCouponQuery;
import com.dragon.modules.yuegou.query.coupon.CouponQuery;
import com.dragon.modules.yuegou.query.coupon.CouponTemplateQuery;
import com.dragon.modules.yuegou.query.coupon.MerchantCouponGrantQuery;
import com.dragon.modules.yuegou.query.coupon.ReceiveDetailRequest;
import com.dragon.modules.yuegou.service.busd.DistrictService;
import com.dragon.modules.yuegou.service.coupon.CouponService;
import com.dragon.modules.yuegou.service.coupon.CouponTemplateRandomService;
import com.dragon.modules.yuegou.service.coupon.CouponTemplateService;
import com.dragon.modules.yuegou.service.coupon.MerchantCouponGrantService;
import com.dragon.modules.yuegou.service.coupon.ParkApplyService;
import com.dragon.modules.yuegou.service.coupon.WelfareService;
import com.dragon.modules.yuegou.service.order.OrderCouponService;
import com.dragon.modules.yuegou.service.product.SkuService;
import com.dragon.modules.yuegou.service.product.SpuService;
import com.dragon.modules.yuegou.service.system.DocumentService;
import com.dragon.modules.yuegou.web.Check;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Nullable;
import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
/**
 * 优惠券（会员的优惠券，不冗余优惠券模板信息，请使用1+N模式查询） 服务实现
 *
 * @author pbuilder generated
 * @date 2019-02-28 18:30:17
 * @version 2.0
 */
@Slf4j
@Service("coupCouponService")
public class CouponServiceImpl extends ServiceImpl<CouponMapper, CouponEntity>
		implements CouponService {

	@Autowired
	private CouponProperties properties;

	@Autowired
	private CouponTemplateService couponTemplateService;

	@Autowired
	private SkuService skuService;

	@Autowired
	private SpuService spuService;

	@Autowired
	private WelfareService welfareService;

	@Autowired
	private OrderCouponService orderCouponService;

	@Autowired
	private MerchantCouponGrantService merchantCouponGrantService;

	@Autowired
	private ParkApplyService parkApplyService;
	
	@Autowired
	private CouponTemplateRandomService couponTemplateRandomService;
	
	@Autowired
	private DocumentService documentService;
	
	@Autowired
	private DistrictService districtService;

	@Autowired
	private ResetTimeProperties resetTimeProperties;

	private static final String COUPON_IDS_REQUIRED = "couponIds is required";

	private static final String BUSDID_REQUIRED = "busdId is required";

	private static final String MEMBER_ID_REQUIRED = "memberId is required";
	
	@PostConstruct
	public void afterInit() {
		log.debug("测试属性 ysq.coupon.hello = {}", properties.getHello());
	}

	@Override
	public IPage<Coupon> couponRecords(PageRequest page, CouponQuery query) {
		IPage<CouponEntity> iPage = new Page<>(page.getPage(),page.getSize(),page.getCount());
		IPage<CouponEntity> entityIPage = baseMapper.selectPage(iPage, query.toQueryWrapper());
		IPage<Coupon> couponIPage = entityIPage.convert(Coupon::new);
		couponIPage.getRecords().stream().forEach(this::fillCouponTemplate);
		return couponIPage;
	}

	@Override
	public Coupon loadDetailById(String id) {
		CouponEntity entity = getById(id);
		Coupon coupon = new Coupon(entity);
		fillCouponTemplate(coupon);
		return coupon;
	}

	@Override
	public List<Coupon> listDetailByIds(Collection<String> ids) {
		// 此处采用1+N查询模式，请确保CouponTemplate开启了缓存
		List<CouponEntity> entities = listByIds(ids);
		List<Coupon> list = entities.stream().map(Coupon::new).collect(Collectors.toList());
		return list.stream().peek(this::fillCouponTemplate).collect(Collectors.toList());

	}

	@Override
	public IPage<Coupon> getMemberParkCouponList(PageRequest page, String busdId, String memberId, CouponStatus status) {
		Check.notEmpty(busdId, BUSDID_REQUIRED);
		Check.notEmpty(memberId, MEMBER_ID_REQUIRED);
		CouponQuery couponQuery = new CouponQuery();
		couponQuery.setMemberId(memberId);
		couponQuery.setBusdId(busdId);
		couponQuery.setType(CouponType.PARK);
		if (status == CouponStatus.NORMAL) {
			couponQuery.setUsed(false);
			couponQuery.setExpired(false);
			couponQuery.expireTime().ge(LocalDateTime.now());
		}
		if (status == CouponStatus.ISUSED) {
			couponQuery.setUsed(true);
		}
		if (status == CouponStatus.ISEXPIRED) {
			couponQuery.expireTime().lt(LocalDateTime.now());
			couponQuery.setExpired(true);
		}
		IPage<CouponEntity> iPage = new Page<>(page.getPage(),page.getSize(),page.getCount());
		return this.page(iPage, couponQuery.toQueryWrapper()).convert(coupon -> fillCouponTemplate(coupon));
	}

	@Override
	public Coupon getByOrderNo(String orderNo) {
		CouponQuery query = new CouponQuery();
		query.orderNo().eq(orderNo);
		List<CouponEntity> list = list(query.toQueryWrapper());
		if (!list.isEmpty()) {
			return new Coupon(list.get(0));
		}
		return new Coupon();
	}

	/**
	 * 给优惠券填充优惠券模板属性
	 * 
	 * @param entity
	 */
	private Coupon fillCouponTemplate(CouponEntity entity) {
		CouponTemplate couponTemplate = couponTemplateService.detail(entity.getCouponTemplateId());

		Coupon coupon = new Coupon(entity);
		coupon.setCouponTemplate(new CouponTemplate(couponTemplate));
		return coupon;
	}

	@Override
	public List<Coupon> listAvaliableCoupons(String memberId, Collection<String> skuIds) {
		Check.notNull(memberId, MEMBER_ID_REQUIRED);
		Check.notEmpty(skuIds, "skuIds is required");
		return listAvaliableCoupons0(memberId, null, skuIds, null);
	}

	@Override
	public List<Coupon> listParkCoupons(String memberId, String parkId, BigDecimal parkFee) {
		Check.notNull(memberId, MEMBER_ID_REQUIRED);
		Check.notNull(parkId, "parkId is required");
		List<Coupon> list = listAvaliableCoupons0(memberId, null, null, parkId);
		if (parkFee != null && parkFee.compareTo(BigDecimal.ZERO) > 0) {
			list = setDefaultSelect(list, parkFee);
		}
		return list;
	}

	private List<Coupon> setDefaultSelect(List<Coupon> list, BigDecimal parkFee) {
		if (list == null) {
			return list;
		}
		if (list.size() == 1) {
			list.get(0).setDefaultSelect(1);
			return list;
		}
		BigDecimal pbd = null;
		Coupon pCoupon = null;
		BigDecimal nbd = null;
		Coupon nCoupon = null;
		for (Coupon coupon : list) {
			// 代金券抵扣金额
			BigDecimal reductionMount = coupon.getCouponTemplate().getReductionMount();
			// 代金券余停车费用抵扣金额
			BigDecimal difference = reductionMount.subtract(parkFee);
			// 停车券抵扣与停车费正好相等
			if (difference.compareTo(BigDecimal.ZERO) == 0) {
				coupon.setDefaultSelect(1);
				return list;
			} else if (difference.compareTo(BigDecimal.ZERO) >0 && (pbd == null || pbd.compareTo(difference) >0)) {
				pbd = difference;
				pCoupon = coupon;
			} else if (nbd == null || nbd.compareTo(difference) <0){
				nbd = difference;
				nCoupon = coupon;
			}
		}
		if (pCoupon != null) {
			pCoupon.setDefaultSelect(1);
		} else if (nCoupon != null) {

			nCoupon.setDefaultSelect(1);
		}
		return list;
	}

	//要求所有的条件都符合，不符合当前需求	
	private List<Coupon> listAvaliableCoupons0(@Nullable String memberId, @Nullable Collection<String> couponIds,
			@Nullable Collection<String> skuIds, @Nullable String parkId) {
		AvailableCouponQuery query = new AvailableCouponQuery();
		query.setMemberId(memberId);
		if (CollectionUtils.isNotEmpty(couponIds)) {
			query.setCouponIdSet(new HashSet<>(couponIds));
		}
		if (CollectionUtils.isNotEmpty(skuIds)) {
			List<SkuEntity> skus = skuService.listByIds(skuIds);
			query.setMemberId(memberId);
			if (!skus.isEmpty()) {
				query.setBusdIdSet(getBusdIdSet(skus));
				query.setMerchantIdSet(getMerchantIdSet(skus));
				query.setProductCategoryIdSet(getProductCategoryIdSet(skus));
				query.setSpuIdSet(getSpuIdSet(skus));
			}
		}
		if (parkId != null) {
			query.setParkIdSet(Collections.singleton(parkId));
		}
		List<Coupon> coupons = baseMapper.selectAvailableCouponList(query);
		coupons.forEach(this::fillCouponTemplate);
		return coupons;
	}

	private List<Coupon> listValidAvaliableCouponDetails(@Nullable String memberId, Collection<String> couponIds,
			@Nullable Collection<String> skuIds, @Nullable String parkId) {
		Check.notNull(couponIds, COUPON_IDS_REQUIRED);
		Set<String> couponIdSet = new HashSet<>(couponIds);
		if (couponIdSet.size() < couponIds.size()) {
			throw new IllegalArgumentException("couponIds has duplicated values : " + couponIds);
		}
		List<Coupon> coupons = listAvaliableCoupons0(memberId, couponIds, skuIds, parkId);
		if (coupons.size() < couponIds.size()) {
			List<String> avaliableCouponIds = coupons.stream().map(Coupon::getId).collect(Collectors.toList());
			throw new DragonException(String.format("有一部分优惠券不可用，全部优惠券: %s，可用的优惠券%s", couponIds, avaliableCouponIds));
		}
		Set<String> memberIds = coupons.stream().map(Coupon::getMemberId).collect(Collectors.toSet());
		if (memberIds.size() > 1) {
			throw new DragonException(String.format("优惠券%s，不全部属于会员%s", couponIds, memberId));
		}
		coupons.forEach(this::fillCouponTemplate);

		validateCoupons(coupons, skuIds);

		return coupons;
	}

	private void validateCoupons(List<Coupon> coupons, @Nullable Collection<String> skuIds) {
		// 实际情况：现阶段只有停车券

		// 约束1：折扣券和抵扣券不能混合使用
		Set<CouponTemplateFormat> formats = coupons.stream().map(c -> c.getCouponTemplate().getFormat())
				.collect(Collectors.toSet());
		if (formats.size() > 1) {
			throw new DragonException("折扣券和抵扣券不能混合使用");
		}
		CouponTemplateFormat format = formats.iterator().next();
		// 约束2：折扣券只能单张使用
		if (CouponTemplateFormat.DISCOUNT.equals(format) && coupons.size() > 1) {
			throw new DragonException("折扣券只能单张使用");
		}

		Set<CouponTemplateType> types = coupons.stream().map(c -> c.getCouponTemplate().getType())
				.collect(Collectors.toSet());
		// 约束3：不同类型的优惠券不能混合使用
		if (types.size() > 1) {
			throw new DragonException("不同类型的优惠券不能混合使用");
		}
		CouponTemplateType type = types.iterator().next();
		// 约束4：停车券不能用于优惠商品
		if (CouponTemplateType.PARK.equals(type) && CollectionUtils.isNotEmpty(skuIds)) {
			throw new DragonException("停车券不能用于优惠商品");
		}
	}

	private void allocateAmount(List<Coupon> coupons, BigDecimal amount) {
		BigDecimal remainAmount = amount;
		for (Coupon coupon : coupons) {
			BigDecimal discountAmount = coupon.getCouponTemplate().calculateDiscountAmount(amount, remainAmount);
			coupon.setDiscountAmount(discountAmount);
			remainAmount = remainAmount.subtract(discountAmount);
		}
	}

	@Override
	public List<Coupon> allocateCoupons(Collection<String> couponIds, Collection<String> skuIds, BigDecimal amount) {
		Check.notNull(skuIds, "skuIds is required");

		List<Coupon> coupons = listValidAvaliableCouponDetails(null, couponIds, skuIds, null);

		allocateAmount(coupons, amount);

		return coupons;
	}

	@Override
	public List<Coupon> allocateParkCoupons(Collection<String> couponIds, String parkId, BigDecimal amount) {
		Check.notNull(parkId, "parkId is required");

		List<Coupon> coupons = listValidAvaliableCouponDetails(null, couponIds, null, parkId);

		allocateAmount(coupons, amount);

		return coupons;
	}

	@Override
	@Transactional
	public void useCoupons(Order order, List<String> couponIds, Boolean isNotify) {
		privateUseCoupons(order,couponIds,isNotify);
	}

	private void privateUseCoupons(Order order, List<String> couponIds, Boolean isNotify){
		Check.notNull(order, "orderNo is required");
		Check.notEmpty(order.getId(), "orderNo is required");
		Check.notEmpty(couponIds, COUPON_IDS_REQUIRED);
		LocalDateTime now = LocalDateTime.now();
		LocalDateTime end = LocalDateTime.now();
		//回调时 用订单号的创建时间
		if (isNotify!=null && isNotify){
			end = order.getOrderTime();
		}


		CouponQuery query = new CouponQuery();
		query.id().in(couponIds);
		query.used().eq(false);
		query.expired().eq(false);
		// 此处依然需要使用时间来判断是否有效
		// 因为定时任务不能做到一到失效时间就立即标记为失效
		query.createdTime().le(now);
		query.expireTime().ge(end);

		Coupon entity = new Coupon();
		entity.setUsed(true);
		entity.setUseTime(LocalDateTime.now());
		entity.setOrderNo(order.getId());

		// 标记【未使用】【未失效】的优惠券为【已使用】
		int usedNum = baseMapper.update(entity, query.toQueryWrapper());
		if (usedNum != couponIds.size()) {
			log.warn("使用优惠券失败，预计使用{}个优惠券，但是只有{}个优惠券能被使用，couponIds = {}", couponIds.size(), usedNum, couponIds);
			throw new DragonException(ErrorCode.CONCURRENT_FAILURE);
		}
	}

	@Override
	@Transactional
	public boolean expireCoupon(String couponId) {
		Check.notEmpty(couponId, "couponId is required");
		CouponQuery query = new CouponQuery();
		query.id().eq(couponId);
		query.used().eq(false);
		query.expired().eq(false);
		query.expireTime().lt(LocalDateTime.now());

		Coupon entity = new Coupon();
		entity.setExpired(true);

		// 标记【未使用】【未失效】但是【失效时间已过】的优惠券为【已失效】
		return baseMapper.update(entity, query.toQueryWrapper()) > 0;
	}

	@Override
	@Transactional
	public void recoverCoupons(List<String> couponIds) {
		Check.notEmpty(couponIds, COUPON_IDS_REQUIRED);
		CouponQuery query = new CouponQuery();
		query.id().in(couponIds);
		Coupon entity = new Coupon();
		entity.setUsed(false);
		baseMapper.update(entity, query.toQueryWrapper());
	}

	private Set<String> getBusdIdSet(List<SkuEntity> skus) {
		Optional<SkuEntity> anyAll = skus.stream().filter(sku -> sku.getBusdId() == null).findAny();
		if (anyAll.isPresent()) {
			// 存在跨商圈的商品
			return Collections.emptySet();
		}
		return skus.stream().map(SkuEntity::getBusdId).collect(Collectors.toSet());
	}

	private Set<String> getMerchantIdSet(List<SkuEntity> skus) {
		Optional<SkuEntity> anyAll = skus.stream().filter(sku -> sku.getMerchantId() == null).findAny();
		if (anyAll.isPresent()) {
			// 存在跨商户的商品
			return Collections.emptySet();
		}
		return skus.stream().map(SkuEntity::getMerchantId).collect(Collectors.toSet());
	}

	private Set<String> getProductCategoryIdSet(List<SkuEntity> skus) {
		List<SpuEntity> spus = spuService.listByIds(getSpuIdSet(skus));
		Optional<SpuEntity> anyAll = spus.stream().filter(spu -> spu.getCategoryId() == null).findAny();
		if (anyAll.isPresent()) {
			// 存在未分类的商品
			return Collections.emptySet();
		}
		return spus.stream().map(SpuEntity::getCategoryId).collect(Collectors.toSet());
	}

	private Set<String> getSpuIdSet(List<SkuEntity> skus) {
		// spuId非空
		return skus.stream().map(SkuEntity::getSpuId).collect(Collectors.toSet());
	}

	@Override
	@Transactional
	public Coupon createCoupon(String orderNo, String memberId, String couponTemplateId, CouponSourceType type) {
		Check.notNull(memberId, MEMBER_ID_REQUIRED);
		Check.notNull(couponTemplateId, "couponTemplateId is required");
		CouponTemplateEntity template = couponTemplateService.getById(couponTemplateId);
		CouponEntity coupon = buildCreateCoupon(orderNo, memberId, template, type);
		baseMapper.insert(coupon);
		return new Coupon(coupon);
	}
	
	private CouponEntity buildCreateCoupon(String orderNo, String memberId, CouponTemplateEntity template, CouponSourceType type){
		LocalDateTime now = LocalDateTime.now();
		CouponEntity coupon = buildCoupon(memberId, orderNo, template, now, null,type.getCode());
		coupon.setSourceType(type);
		coupon.setSource(type.getAlias());
		coupon.setId(IdWorker.getIdStr());
		coupon.setCouponName(template.getTitle());
		// 如果发给用户的是随机券，将特殊处理
		if (template.getFormat().equals(CouponTemplateFormat.RANDOM)) {
			CouponTemplateRandom random = couponTemplateRandomService.getTemplateRandom(template.getId());
			coupon.setReductionMount(random.getReductionMount());
			coupon.setCouponTemplateRandomId(random.getId());
		}
		return coupon;
	}
	
	@Override
	public Coupon createCoupon(String orderNo, String memberId, String merchantId, String couponTemplateId,
			CouponSourceType type) {
		Check.notNull(memberId, MEMBER_ID_REQUIRED);
		Check.notNull(couponTemplateId, "couponTemplateId is required");
		CouponTemplateEntity template = couponTemplateService.getById(couponTemplateId);
		CouponEntity coupon = buildCreateCoupon(orderNo, memberId, template, type);
		coupon.setMerchantId(merchantId);
		baseMapper.insert(coupon);
		return new Coupon(coupon);
	}

	@Override
	public List<Coupon> bachCreateCoupon(String orderNo, String memberId, List<String> couponTemplateIds) {
		Check.notNull(memberId, MEMBER_ID_REQUIRED);
		Check.notEmpty(couponTemplateIds, "couponTemplateIds must not empty");
		Set<String> templateIdSet = new HashSet<>(couponTemplateIds);
		Map<String, CouponTemplateEntity> templateMap = couponTemplateService.listByIds(templateIdSet).stream()
				.collect(Collectors.toMap(CouponTemplateEntity::getId, UnaryOperator.identity()));
		LocalDateTime now = LocalDateTime.now();
		List<CouponEntity> coupons = couponTemplateIds.stream().map(templateMap::get)
				.map(template -> buildCoupon(memberId, orderNo, template, now, null,1)).collect(Collectors.toList());
		super.saveBatch(coupons);
		return coupons.stream().map(Coupon::new).collect(Collectors.toList());
	}

	@Override
	@Transactional
	public int bachCreateCoupon(Set<String> memberIds, String couponTemplateId, String taskName) {
		Check.notNull(memberIds, "memberIds is required");
		Check.notEmpty(couponTemplateId, "couponTemplateId must not empty");
		CouponTemplateEntity template = couponTemplateService.getById(couponTemplateId);
		LocalDateTime now = LocalDateTime.now();
		List<CouponEntity> coupons = memberIds.stream().map(memberId -> buildCoupon(memberId, null, template, now, taskName,2))
				.collect(Collectors.toList());
		super.saveBatch(coupons);

		return coupons.size();
	}

	@Override
	public List<Coupon> issueCoupon(Set<String> memberIds, String couponTemplateId, String taskName,Integer num) {
		Check.notNull(memberIds, "memberIds is required");
		Check.notEmpty(couponTemplateId, "couponTemplateId must not empty");
		CouponTemplateEntity template = couponTemplateService.getById(couponTemplateId);
		LocalDateTime now = LocalDateTime.now();
		List<CouponEntity> coupons = new ArrayList<>();
		for (int i =0 ;i <num;i++){
			coupons.addAll(memberIds.stream().map(memberId -> buildCoupon(memberId, null, template, now, taskName,2))
					.collect(Collectors.toList()));
		}
		super.saveBatch(coupons);
		return coupons.stream().map(Coupon::new).collect(Collectors.toList());
	}

	/**
	 * 构造优惠券
	 * 
	 * @param memberId
	 *            会员ID
	 * @param template
	 *            优惠券模板
	 * @param now
	 *            当前时间
	 * @return 优惠券
	 */
	private CouponEntity buildCoupon(String memberId, String orderNo, CouponTemplateEntity template, LocalDateTime now,
			String source,Integer sourceType) {
		CouponEntity coupon = new CouponEntity();
		coupon.setOwnerType(OwnerType.PLATFORM);
		coupon.setGroupId(template.getGroupId());
		coupon.setBusdId(template.getBusdId());
		coupon.setMerchantId(template.getMerchantId());
		coupon.setMemberId(memberId);
		coupon.setCouponTemplateId(template.getId());
		coupon.setUsed(false);
		coupon.setExpired(false);
		coupon.setOrderNo(orderNo);
		coupon.setSourceType(CouponSourceType.from(sourceType));
		coupon.setSource(source);
		coupon.setType(CouponType.from(template.getType().getCode()));
		coupon.setReductionMount(template.getReductionMount());
		coupon.setUseReductionMount(BigDecimal.ZERO);
		coupon.setCreatedTime(now);
		
		if (CouponTemplateTimeLimitType.FIXED_RANGE.equals(template.getTimeLimitType())) {
			// XXX 把创建时间当开始时间是否有问题？
			coupon.setValidTime(template.getStartTime());
			coupon.setExpireTime(template.getEndTime());
		} else if (CouponTemplateTimeLimitType.VALID_DAYS.equals(template.getTimeLimitType())) {
			coupon.setValidTime(now);
			if (template.getValidHours() != null) {
				coupon.setExpireTime(now.plusHours(template.getValidHours()));
			} else {
				coupon.setExpireTime(now.plusDays(template.getValidDays()));
			}
		} else {
			throw new DragonException("无法创建优惠券，未知的时间限制类型：" + template.getTimeLimitType().getAlias());
		}
		// XXX 订单是否需要记录下单平台？这样就可以在此处判断优惠券来源了
		coupon.setPlatform(CouponPlatform.OTHER);
		return coupon;
	}

	@Override
	public void saveWelfare(Welfare welfare) {
		welfare.setCreatedTime(LocalDateTime.now());
		welfareService.save(welfare);
	}

	@Override
	public List<CouponTemplate> getCouponList(String busdId) {
		CouponTemplateQuery query = new CouponTemplateQuery();
		query.setBusdId(busdId);
		query.setType(CouponTemplateType.PARK);
		List<CouponTemplateEntity> entities = couponTemplateService.list(query.toQueryWrapper());
		return entities.stream().map(CouponTemplate::new).collect(Collectors.toList());
	}

	@Override
	public Welfare getWelfare(String id) {
		if (!StringUtils.isEmpty(id)) {
			WelfareEntity entity = welfareService.getById(id);
			CouponTemplateQuery query = new CouponTemplateQuery();
			query.setId(entity.getCouponTemplateId());
			CouponTemplateEntity couponTemplate = couponTemplateService.getOne(query.toQueryWrapper());

			Welfare welfare = new Welfare(entity);
			welfare.setTitle(couponTemplate.getTitle());
			// 获取领取总数
			welfare.setCounts(welfareService.getCount(id).intValue());
			return welfare;
		} else {
			throw new DragonException(CouponCode.COUPON_ID_IS_NOT_NULL);
		}

	}

	@Override
	@Transactional
	public void parkUseCoupons(Order order, String couponId, Boolean isNotify) {
		List<String> couponIds = new ArrayList<>();
		couponIds.add(couponId);

		privateUseCoupons(order,couponIds,isNotify);

		OrderCouponEntity orderCoupon = new OrderCouponEntity();
		orderCoupon.setOrderNo(order.getId());
		orderCoupon.setCouponId(couponId);
		Coupon coupon = loadDetailById(couponId);
		orderCoupon.setCouponName(coupon.getCouponTemplate().getTitle());
		orderCoupon.setCouponTemplateId(coupon.getCouponTemplateId());
		orderCoupon.setAmount(coupon.getCouponTemplate().getReductionMount());
		orderCouponService.save(orderCoupon);
	}

	@Override
	public void getInvalidNum(List<String> couponIds) {
		// 通过失效的优惠券ID获取这些优惠券的发放信息
		MerchantCouponGrantQuery couponGrantQuery = new MerchantCouponGrantQuery();
		couponGrantQuery.couponId().in(couponIds);
		// 封装停车券申请ID-失效数据列表
		Map<String, List<MerchantCouponGrantEntity>> couponGrantMap = merchantCouponGrantService.list(couponGrantQuery.toQueryWrapper())
				.stream().collect(Collectors.groupingBy(MerchantCouponGrantEntity::getParkApplyId));
		log.debug("couponGrantMap大小 = {}", couponGrantMap.size());
		// 通过停车券申请ID修改废除数量
		ParkApplyEntity parkApply = null;
		List<ParkApplyEntity> updateList = new ArrayList<>();
		for (Entry<String, List<MerchantCouponGrantEntity>> entry : couponGrantMap.entrySet()) {
			log.debug("开始循环map获取值===============");
			parkApply = parkApplyService.getApply(entry.getKey());
			ParkApply updateParkApply = new ParkApply();
			updateParkApply.setId(parkApply.getId());
			if (parkApply.getInvalidNum() != null) {
				updateParkApply.setInvalidNum(parkApply.getInvalidNum() + entry.getValue().size());
			} else {
				updateParkApply.setInvalidNum(entry.getValue().size());
			}

			updateList.add(updateParkApply);
		}
		log.debug("updateList大小={}", updateList.size());
		parkApplyService.updateBatchById(updateList);
		log.debug("批量修改结束");
	}
	
	private CouponTemplate couponCombination(CouponTemplate couponTemplate){
		List<Coupon> couponList = couponTemplate.getCouponList();
		log.debug("券模板使用id={},title={},重叠={}", couponTemplate.getId(), couponTemplate.getTitle(), couponTemplate.getAllowCpys());
		log.debug("券数量={}", couponList.size());
		
		//允许重叠使用
		if(couponTemplate.getAllowCpys()){
			List<List<Integer>> indexList = getCombinList(couponList.size());
			List<CouponDiscountCombin> couponDiscountAbsList = getCouponDiscountAbsList(couponList, indexList, couponTemplate.getAllowSkuFee());
			couponDiscountAbsList = couponDiscountAbsList.stream()
					.sorted(Comparator.comparing(CouponDiscountCombin::getCouponDiscountFee).reversed()
							.thenComparing(CouponDiscountCombin::getDiscountAbs))
					.collect(Collectors.toList());
			CouponDiscountCombin couponDiscountCombin = couponDiscountAbsList.get(0);
			couponTemplate.setCouponList(couponDiscountCombin.getCouponList());
			couponTemplate.setOrderDiscountAbs(couponDiscountCombin.getDiscountAbs());
			couponTemplate.setCouponDiscountFee(couponDiscountCombin.getCouponDiscountFee());
		}else{
			//单张使用
			couponList = couponList.stream().map(coupon-> {
					BigDecimal discountFee = coupon.getReductionMount().subtract(couponTemplate.getAllowSkuFee());
					coupon.setDiscountAmount(discountFee.compareTo(BigDecimal.ZERO)==-1?coupon.getReductionMount():couponTemplate.getAllowSkuFee());
					coupon.setUseReductionMount(discountFee.abs());
					return coupon;
				}
			).sorted(Comparator.comparing(Coupon::getDiscountAmount).reversed().thenComparing(Coupon::getUseReductionMount))
			.collect(Collectors.toList());
			couponList.get(0).setUsed(true);
			couponTemplate.setCouponList(couponList);
			couponTemplate.setOrderDiscountAbs(couponList.get(0).getUseReductionMount());
			couponTemplate.setCouponDiscountFee(couponList.get(0).getDiscountAmount());
		}
		couponTemplate.setCouponList(couponTemplate.getCouponList().stream().filter(Coupon::getUsed).collect(Collectors.toList()));
		return couponTemplate;
	}

	private List<CouponDiscountCombin> getCouponDiscountAbsList(List<Coupon> couponList, List<List<Integer>> indexList, BigDecimal allowSkuFee) {
		List<CouponDiscountCombin> result = new ArrayList<>();
		for (List<Integer> list : indexList) {
			CouponDiscountCombin couponDiscountCombin = new CouponDiscountCombin();
			List<Coupon> combinItemList = getCouponListByIndexs(couponList, list);
			couponDiscountCombin.buildCombin(combinItemList, allowSkuFee);
			result.add(couponDiscountCombin);
		}
		return result;
	}

	private List<Coupon> getCouponListByIndexs(List<Coupon> couponList, List<Integer> list) {
		List<Coupon> result = new ArrayList<>();
		for (Integer index : list) {
			Coupon coupon = couponList.get(index);
			coupon.setUsed(true);
			result.add(coupon);
		}
		return result;
	}

	/**
	 * 获取排列数
	 */
	private List<List<Integer>> getCombinList(int size) {
		//当列表只有一个值时 求不出排列数
		List<String> indexStrList = IntStream.range(0, size > 15 ? 15 : size).boxed().map(x -> x + "")
				.collect(Collectors.toList());
		List<String[]> collList = new ArrayList<>();
		for (int i = 1; i <= indexStrList.size(); i++) {
			collList.addAll(MathUtil.combinationSelect(indexStrList.toArray(new String[] {}), i));
		}
		return collList.stream().map(ids -> Arrays.asList(ids).stream().map(Integer::parseInt).collect(Collectors.toList())).collect(Collectors.toList());
	}

	@Override
	public List<Coupon> getBestCoupon(String busdId, String memberId, BigDecimal orderFee, List<Sku> orderSkuList) {
		Check.notNull(busdId, BUSDID_REQUIRED);
		Check.notNull(memberId, MEMBER_ID_REQUIRED);
		Check.notNull(orderSkuList, "skuIdList is required");
		// 获取用户的可用券列表
		List<Coupon> memberCouponList = memberCouponList(busdId, memberId);
		if (log.isDebugEnabled() && CollectionUtils.isNotEmpty(memberCouponList)) {
			List<String> couponIdList = memberCouponList.stream().map(Coupon::getId).collect(Collectors.toList());
			log.debug("getBestCoupon memberCouponList ={}", JSON.toJSONString(couponIdList));
		}
		List<CouponTemplate> templateList = memberCouponList.stream()
				.collect(Collectors.groupingBy(Coupon::getCouponTemplateId))
				.entrySet()
				.stream()
				.map(entry -> {
					CouponTemplate template = couponTemplateService.detail(entry.getKey());
					template.buildDiscountItem(getCouponSkuList(template, orderSkuList));
					template.setCouponList(entry.getValue());
					return template;
				}).filter(couponTemplate -> CollectionUtils.isNotEmpty(couponTemplate.getAllowSkuList())
						&& couponTemplate.getAllowSkuList().size() > 0
						&& couponTemplate.getAllowSkuFee() != null
						&& couponTemplate.getAllowSkuFee().compareTo(BigDecimal.ZERO) > 0)
				.filter(couponTemplate -> couponTemplate.getLowerLimit().signum() == -1 || couponTemplate.getLowerLimit().compareTo(orderFee) < 1).map(couponTemplate -> couponCombination(couponTemplate))
				.sorted(Comparator.comparing(CouponTemplate::getCouponDiscountFee).reversed()
						.thenComparing(CouponTemplate::getOrderDiscountAbs))
				.collect(Collectors.toList());
		if (log.isDebugEnabled() && CollectionUtils.isNotEmpty(templateList)) {
			log.debug("getBestCoupon templateList ={}", JSON.toJSONString(templateList));
		}
		return templateList.isEmpty() ? Collections.emptyList() : templateList.get(0).getCouponList();
	}
	
	public List<Coupon> getBestCoupon2(String busdId, String memberId, BigDecimal orderFee, List<Sku> orderSkuList) {
		Check.notNull(busdId, BUSDID_REQUIRED);
		Check.notNull(memberId, MEMBER_ID_REQUIRED);
		Check.notNull(orderSkuList, "skuIdList is required");
		//获取用户的可用券列表
		List<Coupon> memberCouponList = memberCouponList(busdId, memberId);
		//每一个优惠券模板对应商品列表分别能抵扣的最高金额
		Map<String, BigDecimal> discountMap = new HashMap<>();
		Map<String, List<Coupon>> discountCoupon = new HashMap<>();
		
		memberCouponList.stream()
		.collect(Collectors.groupingBy(Coupon::getCouponTemplateId))
		.entrySet()
		.stream()
		.filter(entry-> couponTemplateService.verifyOrderCash(entry.getKey(), orderFee))
		.forEach(entry -> {
			CouponTemplate couponTemplate = couponTemplateService.detail(entry.getKey());
			//去掉不可重复使用的券
			List<Coupon> groupCouponList = entry.getValue();
			// 获取券模板可用的商品列表
			log.debug("订单商品列表={}",JSONObject.toJSONString(orderSkuList));
			List<Sku> hasList = getCouponSkuList(couponTemplate, orderSkuList);
			log.debug("券可用商品列表={}",JSONObject.toJSONString(hasList));
			BigDecimal discountFee = countCouponDiscountFee(couponTemplate, groupCouponList, hasList);
			//将最大有效优惠金额放入MAP
			if (discountMap.isEmpty() || discountMap.values().iterator().next().compareTo(discountFee) == -1) {
				discountMap.clear();
				discountMap.put(entry.getKey(), discountFee);
				discountCoupon.clear();
				discountCoupon.put(entry.getKey(), groupCouponList.stream().filter(Coupon::getUsed).collect(Collectors.toList()));
			}	
		});
		return getFirstOrNull(discountCoupon);
	}

	private List<Coupon> getFirstOrNull(Map<String, List<Coupon>> map) {
		List<Coupon> obj = null;
		for (Entry<String, List<Coupon>> entry : map.entrySet()) {
			obj = entry.getValue();
			if (obj != null) {
				break;
			}
		}
		return obj;
	}
	
	private BigDecimal countCouponDiscountFee(CouponTemplate couponTemplate, List<Coupon> couponList, List<Sku> skuList) {
		log.debug("countCouponDiscountFee,couponTemplateId={}",couponTemplate.getId());
		log.debug("countCouponDiscountFee,couponList={}", JSON.toJSONString(couponList));
		log.debug("countCouponDiscountFee,skuList={}",JSON.toJSONString(skuList));
		
		BigDecimal sumFee = skuList.stream().map(sku-> sku.getDiscountPrice().multiply(new BigDecimal(sku.getBuyNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
		BigDecimal discountFee = BigDecimal.ZERO;
		//券允许重叠使用
		if(couponTemplate.getAllowCpys()){
			for (Coupon coupon : couponList) {
				// 折扣金额小于订单金额
				if (discountFee.compareTo(sumFee) <0) {
					//累加折扣金额
					discountFee = discountFee.add(coupon.getReductionMount());
					coupon.setUsed(true);
				}else {
					log.debug("1折扣金额大于或等于订单金额订单金额={},优惠金额={}", sumFee, discountFee);
					return sumFee;
				}
			}
		}else{
			return getAllowCpys(sumFee,couponList);
		}
		return discountFee;
	}

	private BigDecimal getAllowCpys(BigDecimal sumFee,List<Coupon> couponList){
		//券不允许重叠使用，只能使用单张
		Coupon temp = null;
		BigDecimal calAbsTemp = sumFee;
		for (Coupon coupon : couponList) {
			//计算订单金额与单张金额差的绝对值
			BigDecimal calAbs = sumFee.subtract(coupon.getReductionMount()).abs();
			log.debug("sumFee={},calAbsTemp={},calAbs={},", sumFee, calAbsTemp, calAbs);
			//当差额为0时  优惠券正好能抵扣订单金额
			if (calAbs.compareTo(BigDecimal.ZERO) == 0) {
				coupon.setUsed(true);
				return sumFee;
			} else {
				if (calAbsTemp.compareTo(calAbs) > 0) {
					temp = coupon;
					calAbsTemp = calAbs;
				}
			}
		}
		couponList.remove(temp);
		temp.setUsed(true);
		couponList.add(temp);
		return temp.getReductionMount();
	}

	public List<Sku> getCouponSkuList(CouponTemplate couponTemplate, List<Sku> skuList) {
		if (couponTemplate.getScope() == CouponTemplateScope.ALL) {
			return skuList;
		} else if (couponTemplate.getScope() == CouponTemplateScope.SPU) {
			return skuList.stream().filter(sku -> couponTemplate.scopeSpuIdList().contains(sku.getSpuId())).collect(Collectors.toList());
		} else if (couponTemplate.getScope() == CouponTemplateScope.EXCLUDE_SPU) {
			List<Sku> temp = new ArrayList<>();
			temp.addAll(skuList);
			for (Sku sku : skuList) {
				if (couponTemplate.scopeSpuIdList().contains(sku.getSpuId())) {
					temp.remove(sku);
				}
			}
			return temp;
		}
		return new ArrayList<>();
	}

	private List<Coupon> memberCouponList(String busdId, String memberId) {
		CouponQuery query = new CouponQuery();
		query.memberId().eq(memberId);
		query.busdId().eq(busdId);
		query.type().eq(CouponType.COUPON);
		query.used().eq(false);
		query.expired().eq(false);
		query.expireTime().gt(LocalDateTime.now());

		QueryWrapper<CouponEntity> wrapper = query.toQueryWrapper();
		wrapper.orderByDesc(CouponEntity.COL_REDUCTION_MOUNT);
		wrapper.orderByAsc(CouponEntity.COL_EXPIRE_TIME);
		wrapper.orderByAsc(CouponEntity.COL_CREATED_TIME);
		List<CouponEntity> list1 = list(wrapper);
		List<Coupon> list = new ArrayList<>();
		if (CollectionUtils.isNotEmpty(list1)) {
			for (CouponEntity coupon : list1) {
				String couponTemplateId = coupon.getCouponTemplateId();
				CouponTemplate template = couponTemplateService.detail(couponTemplateId);
				LocalDateTime startTime = template.getStartTime();
				if (startTime == null || startTime.compareTo(LocalDateTime.now()) <= 0) {
					list.add(new Coupon(coupon));
				}
			}
		}
		return list;
	}

	@Override
	public List<CouponUserList> couponUserList(String id, String mobile) {
		return baseMapper.couponUserList(id,mobile);
	}

	@Override
	public IPage<ReceiveDetail> receiveDetail(PageRequest page, ReceiveDetailRequest request) {
		IPage<ReceiveDetail> iPage = new Page<>(page.getPage(),page.getSize(),page.getCount());
		return baseMapper.receiveDetail(iPage, request);
	}

	@Override
	public Document exportReceiveData(ReceiveDetailRequest request) {
		List<ReceiveDetail> list = baseMapper.receiveDetail(request);
		ReceiveDataWorkbook workbook = new ReceiveDataWorkbook();
		List<ReceiveDataWorkbook.CouponRow> coupons = new ArrayList<>();
		list.stream().forEach(detail -> {
			ReceiveDataWorkbook.CouponRow coupon = new ReceiveDataWorkbook.CouponRow();
			BeanUtils.copyProperties(detail, coupon);
			coupon.setUsed(detail.getUsed() == 0 ? "未使用" : "已使用");
			coupons.add(coupon);
		});
		workbook.setCoupon(coupons);
		return documentService.generateExcel("优惠劵领取数据明细表.xlsx", workbook);
	}

	@Override
	public void expiryWarning() {
		CouponQuery query = new CouponQuery();
		query.setType(CouponType.COUPON);
		query.setUsed(false);
		query.setExpired(false);
		query.expireTime().gt(LocalDateTime.now());
		List<CouponEntity> list = list(query.toQueryWrapper());
		// 查询优惠劵模板
		// 筛选需要预警的优惠劵
		LocalDateTime now = LocalDateTime.now();
		List<CouponEntity> collect = list.stream().filter(coupon -> {
			CouponTemplate template = couponTemplateService.detail(coupon.getCouponTemplateId());
			Long hours = Duration.between(now, coupon.getExpireTime()).toHours();
			return template.getExpireWarnHour() != null && new Long(template.getExpireWarnHour()).equals(hours);
		}).collect(Collectors.toList());
		// 推送预警消息
	}

	@Override
	public Long couponNum(String busdId, String memberId, CouponType couponType){
		CouponQuery couponQuery = new CouponQuery();
		couponQuery.setMemberId(memberId);
		couponQuery.setBusdId(busdId);
		couponQuery.setType(couponType);
		couponQuery.setUsed(false);
		couponQuery.setExpired(false);
		couponQuery.expireTime().gt(LocalDateTime.now());
		return this.count(couponQuery.toQueryWrapper());
	}
}